/**
 * Copyright 2007-2008 Vidar Svansson
 *     
 * This file is part of JAdapter.
 * 
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jadapter.adapters;

import org.jadapter.exceptions.AdapterConfigurationException;
import org.jadapter.registry.TransformerRegistry;
import org.jadapter.Adapter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;

/**
 * Abstract adapter that determines the type information based on reflection.
 * <br></br><br></br>
 * 
 * The factory class must adhere to a specific pattern for this to work.
 * <pre>
 * public class FooBar implements Bar {
 *     
 *     private Foo foo;
 * 
 *     public FooBar(Foo foo) { 
 *         this.foo = foo;
 *     }
 *     
 *     // ... implementation of Bar using the foo context here
 * }
 * </pre>
 * 
 * The important bits here is that the class implements <code>Bar</code> and depends on <code>Foo</code>.
 * We infer from this that this class can adapt <code>Foo</code> to <code>Bar</code>.
 * 
 * @author Vidar Svansson
 * 
 * @since 0.2
 *
 * @param <I>
 * @param <C>
 */
public abstract class AutoConfigAdapter<I, C> extends AbstractAdapter<I, C> {

    private Class<?> factory;



    public AutoConfigAdapter(Class<?> factory) {
        this.factory = factory;
    }

    protected Class<?> getFactory() {
        return factory;
    }

    /**
     * @throws AdapterConfigurationException if it can not determine the type.
     */
    @Override
    public Type from() {
        Type from = super.from();
        if (from == null && factory != null) {
            boolean resolved = false;
            for (Constructor<?> constructor : factory.getConstructors()) {
                Class<?>[] types = constructor.getParameterTypes();

                if (types.length == 1) {
                    if (resolved) {
                        throw new AdapterConfigurationException(
                                "Could not resolve type information for C in class "
                                        + factory
                                        + " the adapter must only declare one single arg"
                                        + " constructor or manually declare the type of C");
                    }
                    from = types[0];
                    resolved = true;
                } else if(types.length == 2) {
                	if(types[1].isAssignableFrom(TransformerRegistry.class)){
                        from = types[0];
                        resolved = true;
                	} 
                }
            }
            if (resolved) {
                from(from);
            } else {
                throw new AdapterConfigurationException(
                        "Could not resolve type information for C in class "
                                + getFactory()
                                + " no suitable suitable single arg constructors found");
            }

        }
        return from;
    }


    /**
     * @throws AdapterConfigurationException if it can not determine the type.
     */
    @Override
    public Type to() {
        Type to = super.to();
        if (to == null && getFactory() != null) {
            Class<?>[] interfaces = factory.getInterfaces();
            if (interfaces.length == 1) {
                to = interfaces[0];
                to(to);
            } else
                throw new AdapterConfigurationException(
                        "Could not resolve type information for I in class "
                                + getFactory()
                                + " the adapter must implement only one interface or "
                                + "manually declare the type of I");
        }
        return to;
    }

    /**
     * @deprecated temporary method while deprecating setFactory in JAdapter.
     * @param factory
     */
    protected void setFactory(Class<?> factory) {
        this.factory = factory;
    }
}
